import 'dart:collection';
import 'dart:convert';

import 'package:flutter_book/config/config.dart';
import 'package:flutter_book/data/model/yck/book_info_rule.dart';
import 'package:flutter_book/data/model/yck/book_type.dart';
import 'package:flutter_book/data/model/yck/content_rule.dart';
import 'package:flutter_book/data/model/yck/explore_rule.dart';
import 'package:flutter_book/data/model/yck/rule/rule.dart';
import 'package:flutter_book/data/model/yck/rule/rule_factory.dart';
import 'package:flutter_book/data/model/yck/search_rule.dart';
import 'package:flutter_book/data/model/yck/toc_rule.dart';
import 'package:flutter_book/data/model/yck/yck_source_book_any_model.dart';
import 'package:flutter_book/data/model/yck/yck_source_book_model.dart';

class AnalyzeYckBookSourceUtil {
  var headerPattern = RegExp("@Header:\\{.+?\\}");
  var jsPattern = RegExp("\\{\\{.+?\\}\\}");

  static AnalyzeYckBookSourceUtil? _util;

  static AnalyzeYckBookSourceUtil init() {
    if (_util == null) {
      _util = AnalyzeYckBookSourceUtil();
    }
    return _util!;
  }

  YckSourceBookModel? analyzeYckBookSource(dynamic json) {
    YckSourceBookAnyModel? anyModel;

    Map<String, dynamic> jsonItem = Map();
    try {
      anyModel = YckSourceBookAnyModel.fromJson(json);
      if (json is Map<String, dynamic>) {
        jsonItem = json;
      } else if (json is String) {
        jsonItem = jsonDecode(json);
      } else {
        return null;
      }
    } catch (e) {
      return null;
    }

    try {
      if (anyModel.ruleToc == null) {
        if (jsonItem["bookSourceUrl"] == null) {
          return null;
        }

        var content = jsonItem["ruleBookContent"] ?? "";
        if (content.startsWith("\$") && !content.startsWith("\$.")) {
          content = content.substring(1);
        }

        return YckSourceBookModel(
            jsonItem["bookSourceName"] ?? "",
            jsonItem["bookSourceGroup"],
            jsonItem["bookSourceUrl"] ?? "",
            jsonItem["bookSourceType"] == "AUDIO" ? BookType.audio : BookType.defaultValue,
            jsonItem["ruleBookUrlPattern"],
            null,
            jsonItem["serialNumber"] ?? 0,
            jsonItem["enable"] ?? true,
            toNewUrls(jsonItem["ruleFindUrl"]) != null,
            uaToHeader(jsonItem["httpUserAgent"]),
            jsonItem["loginUrl"],
            jsonDecode(jsonItem["loginUi"]),
            jsonItem["loginCheckJs"],
            jsonItem["bookSourceComment"] ?? "",
            0,
            0,
            toNewUrls(jsonItem["ruleFindUrl"]),
            ExploreRule(
                toNewRule(jsonItem["ruleFindList"]),
                toNewRule(jsonItem["ruleFindName"]),
                toNewRule(jsonItem["ruleFindAuthor"]),
                toNewRule(jsonItem["ruleFindIntroduce"]),
                toNewRule(jsonItem["ruleFindKind"]),
                toNewRule(jsonItem["ruleFindLastChapter"]),
                null,
                toNewRule(jsonItem["ruleFindNoteUrl"]),
                toNewRule(jsonItem["ruleFindCoverUrl"]),
                null),
            toNewUrl(jsonItem["ruleSearchUrl"]),
            SearchRule(
                toNewRule(jsonItem["ruleSearchList"]),
                toNewRule(jsonItem["ruleSearchName"]),
                toNewRule(jsonItem["ruleSearchAuthor"]),
                toNewRule(jsonItem["ruleSearchIntroduce"]),
                toNewRule(jsonItem["ruleSearchKind"]),
                toNewRule(jsonItem["ruleSearchLastChapter"]),
                null,
                toNewRule(jsonItem["ruleSearchNoteUrl"]),
                toNewRule(jsonItem["ruleSearchCoverUrl"]),
                null),
            BookInfoRule(
                toNewRule(jsonItem["ruleBookInfoInit"]),
                toNewRule(jsonItem["ruleBookName"]),
                toNewRule(jsonItem["ruleBookAuthor"]),
                toNewRule(jsonItem["ruleIntroduce"]),
                toNewRule(jsonItem["ruleBookKind"]),
                toNewRule(jsonItem["ruleBookLastChapter"]),
                null,
                toNewRule(jsonItem["ruleCoverUrl"]),
                toNewRule(jsonItem["ruleChapterUrl"]),
                null,
                null),
            TocRule(toNewRule(jsonItem["ruleChapterList"]), toNewRule(jsonItem["ruleChapterName"]),
                toNewRule(jsonItem["ruleContentUrl"]), null, null, toNewRule(jsonItem["ruleChapterUrlNext"])),
            ContentRule(toNewRule(content), toNewRule(jsonItem["ruleContentUrlNext"]), null, null,
                toNewRule(jsonItem["ruleBookContentReplace"]), null));
      } else {
        String? loginUrl;
        if (anyModel.loginUrl == null) {
          loginUrl = null;
        } else if (anyModel.loginUrl is String) {
          loginUrl = anyModel.loginUrl.toString();
        } else if (anyModel.loginUrl is Map<String, dynamic>) {
          loginUrl = (anyModel.loginUrl as Map<String, dynamic>)["url"];
        } else {
          loginUrl = null;
        }

        return YckSourceBookModel(
            anyModel.bookSourceName,
            anyModel.bookSourceGroup,
            anyModel.bookSourceUrl,
            anyModel.bookSourceType,
            anyModel.bookUrlPattern,
            null,
            anyModel.customOrder,
            anyModel.enabled,
            anyModel.enabledExplore,
            anyModel.header,
            loginUrl,
            anyModel.loginUi,
            anyModel.loginCheckJs,
            anyModel.bookSourceComment,
            anyModel.lastUpdateTime,
            anyModel.weight,
            anyModel.exploreUrl,
            ExploreRule.fromJson(anyModel.ruleExplore),
            anyModel.searchUrl,
            SearchRule.fromJson(anyModel.ruleSearch),
            BookInfoRule.fromJson(anyModel.ruleBookInfo),
            TocRule.fromJson(anyModel.ruleToc),
            ContentRule.fromJson(anyModel.ruleContent));
      }
    } catch (e) {}
    return null;
  }

  // default规则适配
  // #正则#替换内容 替换成 ##正则##替换内容
  // | 替换成 ||
  // & 替换成 &&
  Rule? toNewRule(String? oldRule) {
    if (oldRule == null) return null;
    var newRule = oldRule;
    var reverse = false;
    var allinone = false;

    if (oldRule.startsWith("-")) {
      reverse = true;
      newRule = oldRule.substring(1);
    }

    if (newRule.startsWith("+")) {
      allinone = true;
      newRule = newRule.substring(1);
    }

    if (!(newRule.toUpperCase()).startsWith("@CSS:".toUpperCase()) &&
        !(newRule.toUpperCase()).startsWith("@XPath:".toUpperCase()) &&
        !newRule.startsWith("//") &&
        !newRule.startsWith("##") &&
        !newRule.startsWith(":") &&
        !(newRule.toUpperCase()).contains("@js:".toUpperCase()) &&
        !(newRule.toUpperCase()).contains("<js>".toUpperCase())) {
      if (newRule.contains("#") && !newRule.contains("##")) {
        newRule = oldRule.replaceAll("#", "##");
      }

      if (newRule.contains("|") && !newRule.contains("||")) {
        if (newRule.contains("##")) {
          var list = newRule.split("##");
          if (list[0].contains("|")) {
            newRule = list[0].replaceAll("|", "||");
            for (int i = 1; i < list.length; i++) {
              newRule += "##" + list[i];
            }
          }
        } else {
          newRule = newRule.replaceAll("|", "||");
        }
      }
      if (newRule.contains("&") && !newRule.contains("&&") && !newRule.contains("http") && !newRule.startsWith("/")) {
        newRule = newRule.replaceAll("&", "&&");
      }
    }

    if (allinone) {
      newRule = "+$newRule";
    }
    if (reverse) {
      newRule = "-$newRule";
    }
    return RuleFactory.parse(newRule);
  }

  String? toNewUrls(String? oldUrls) {
    if (oldUrls == null) return null;
    if (oldUrls.startsWith("@js:") || oldUrls.startsWith("<js>")) return oldUrls;
    if (!oldUrls.contains("\n") && !oldUrls.contains("&&")) return toNewUrl(oldUrls);

    var urls = oldUrls.split(RegExp("(&&|\r?\n)+"));

    String newUrls = "";

    for (int i = 0; i < urls.length; i++) {
      String? toNewUrlString = toNewUrl(urls[i]);
      if (toNewUrlString == null) {
        continue;
      }
      newUrls += toNewUrlString.replaceAll(RegExp("\n\\s*"), "");

      if (i != urls.length - 1) {
        newUrls += "\n";
      }
    }

    if (newUrls == "") {
      return null;
    }
    return newUrls;
  }

  String? toNewUrl(String? oldUrl) {
    if (oldUrl == null) return null;

    var url = oldUrl;

    if (oldUrl.toUpperCase().startsWith("<js>".toUpperCase())) {
      url = url.replaceAll("=searchKey", "={{key}}").replaceAll("=searchPage", "={{page}}");
      return url;
    }

    var map = HashMap<String, String>();
    var mather = headerPattern.stringMatch(url);
    if (headerPattern.hasMatch(url) && mather != null) {
      url = url.replaceAll(mather, "");
      map["headers"] = mather.substring(8);
    }

    var urlList = url.split("|");
    url = urlList[0];
    if (urlList.length > 1) {
      map["charset"] = urlList[1].split("=")[1];
    }

    mather = jsPattern.stringMatch(url);
    var jsList = <String>[];
    while (jsPattern.hasMatch(url) && mather != null) {
      jsList.add(mather);
      url = url.replaceAll(jsList.last, "\$${jsList.length - 1}");
      mather = jsPattern.stringMatch(url);
    }

    url = url.replaceAll("{", "<").replaceAll("}", ">");
    url = url.replaceAll("searchKey", "{{key}}");
    url = url
        .replaceAll(RegExp("<searchPage([-+]1)>"), "{{page\$1}}")
        .replaceAll(RegExp("searchPage([-+]1)"), "{{page\$1}}")
        .replaceAll("searchPage", "{{page}}");

    for (int i = 0; i < jsList.length; i++) {
      url = url.replaceAll("\$$i", jsList[i].replaceAll("searchKey", "key").replaceAll("searchPage", "page"));
    }

    urlList = url.split("@");
    url = urlList[0];

    if (urlList.length > 1) {
      map["method"] = "POST";
      map["body"] = urlList[1];
    }
    if (map.length > 0) {
      url += "," + jsonEncode(map);
    }
    return url;
  }

  String? uaToHeader(String? ua) {
    if (ua == null) return null;
    Map<String, dynamic> map = Map();
    map[AppConfig.UA_NAME] = ua;
    return jsonEncode(map);
  }
}
